<?php
/**
 * Short news articles manager.
 *
 * @author Martin "DevelX" Jurča
 */
class News extends Module {
    protected $name = 'News';
    protected $administrable = true;
    protected $version = '2009-08-20';
    protected $details = array('dependencies' => array('Categories'),
            'observes' => array('News'));

    const NEWS_TABLE  = 'news';
    const NEWS_CATEGORIES = 'newsCategories';

    private $newsTable = array('id' => 'iPN+', 'title' => "v'512'N", 'lang' =>
            "v'5'", 'image' => 'i', 'text' => 'TN', 'created' => 'iN',
            'edited' => 'i', 'source' => 's', 'author' => 's');
    private $categoryTable = array('news' => 'iNI', 'category' => 'iNI');

    private $newsRecord = array('table' => self::NEWS_TABLE, 'data' =>
            array());
    private $categoryRecord = array('table' => self::NEWS_CATEGORIES, 'data' =>
            array());

    private $filesRoot;
    private $categoryRoot;

    public function  __construct(DBAPI $dbapi, RecordList $recordList) {
        parent::__construct($dbapi, $recordList);
        $this->newsRecord['properties'] = $this->newsTable;
        $this->categoryRecord['properties'] = $this->categoryTable;
        $settings = @unserialize(file_get_contents($this->getDir(
                '/settings')));
        if ($settings !== false) {
            $this->filesRoot = $settings['filesRoot'];
            $this->categoryRoot = $settings['categoryRoot'];
        }
    }

    public function install() {
        $this->dbdriver->createTable(self::NEWS_TABLE, $this->newsTable);
        $this->dbdriver->createTable(self::NEWS_CATEGORIES,
                $this->categoryTable);
        Modules::getModule('Users')->registerRights($this->name,
                $this->getRights());
        FTP::createFile($this->getDir('/settings', true));
        $file = @fopen($this->getDir('/settings'), 'w');
        if (!is_resource($file)) {
            return false;
        }
        @fwrite($file, serialize(array('filesRoot' => 0, 'categoryRoot' =>
                0)));
        @fclose($file);
        return true;
    }

    public function uninstall() {
        $this->dbdriver->dropTable(self::NEWS_TABLE);
        $this->dbdriver->dropTable(self::NEWS_CATEGORIES);
        FTP::deleteFile($this->getDir('/settings', true));
        return true;
    }

    public function initDisplay($param = null) {
        return;
        $details = Modules::getModule('View')->getUrl();
        if (Kernel::getMode() == Kernel::ADMIN) {
            if (count($details) == 2) {
                if ($details[1] == 'add-news') {
                    Modules::getModule('View')->addJS(Config::WEB_DIR .
                            '/utils/tiny_mce/tiny_mce.js');
                    Modules::getModule('View')->addJS(Config::WEB_DIR .
                            $this->getDir('/admin.js', true));
                }
            } elseif (count($details) == 3) {
                Modules::getModule('View')->addJS(Config::WEB_DIR .
                        '/utils/tiny_mce/tiny_mce.js');
                Modules::getModule('View')->addJS(Config::WEB_DIR .
                        $this->getDir('/admin.js', true));
            }
        } else {
            if (count($details) == 2) {
                Modules::getModule('View')->addCSS(Config::WEB_DIR .
                        '/utils/lightbox/lightbox.css');
                Modules::getModule('View')->addJS(Config::WEB_DIR .
                        '/utils/lightbox/prototype.js');
                Modules::getModule('View')->addJS(Config::WEB_DIR . '/utils/' .
                        'lightbox/scriptaculous.js?load=effects,builder');
                Modules::getModule('View')->addJS(Config::WEB_DIR .
                        '/utils/lightbox/lightbox.js');
            }
        }
    }

    public function display($param = null) {
        if (Kernel::getMode() == Kernel::ADMIN) {
            $this->displayAdmin($param);
        } else {
            $this->displayPublic($param);
        }
    }

    public function &onDelete(&$details, $source) {
        $result = null;
        Kernel::setRedirect($this->getLink());
        if (!Modules::getModule('Users')->checkRights('News', 'delete news')) {
            Modules::getModule('View')->addMessage($this->lang(
                    'insufficentRights'), View::MSG_ERROR);
            return $result;
        }
        $id = (int) substr($details, 0, -1);
        $this->dbdriver->delete($this->categoryRecord['table'], '`news` = ' .
                $id);
        $this->dbdriver->delete($this->newsRecord['table'], '`id` = ' . $id);
        return $result;
    }

    public function &onCreateNews(&$details, $source) {
        $result = null;
        Kernel::setRedirect($this->getLink());
        if (!Modules::getModule('Users')->checkRights('News', 'create news')) {
            Modules::getModule('View')->addMessage($this->lang(
                    'insufficentRights'), View::MSG_ERROR);
            return $result;
        }
        $news = $this->newsRecord;
        $news['data'] = array('title' => $details['title'], 'lang' =>
                $details['lang'], 'text' => $details['text'], 'source' =>
                $details['newsSource'], 'author' => $details['author'],
                'created' => time());
        $image = &Modules::getModule('Files')->uploadFiles($_FILES,
                $this->filesRoot);
        if (count($image) > 0) {
            $news['data']['image'] = $image[0]['data']['id'];
        }
        $this->dbapi->save($news);
        if (isset($details['category']) && is_array($details['category'])) {
            foreach ($details['category'] as $categoryId) {
                $category = $this->categoryRecord;
                $category['data'] = array('news' => $news['data']['id'],
                        'category' => $categoryId);
                $this->dbapi->save($category);
            }
        }
        Modules::getModule('RSS')->pushItem($details['title'], 'http://' .
                    'www.' . Config::DOMAIN_NAME . Config::WEB_DIR .
                    $this->getLink(), strip_tags($details['text']));
        return $result;
    }

    public function &onEditNews(&$details, $source) {
        $result = null;
        Kernel::setRedirect($this->getLink());
        if (!Modules::getModule('Users')->checkRights('News', 'edit news')) {
            Modules::getModule('View')->addMessage($this->lang(
                    'insufficentRights'), View::MSG_ERROR);
            return $result;
        }
        $this->dbdriver->delete($this->categoryRecord['table'], '`news` = ' .
                (int) $details['id']);
        if (isset($details['category']) && is_array($details['category'])) {
            foreach ($details['category'] as $categoryId) {
                $category = $this->categoryRecord;
                $category['data'] = array('news' => $details['id'],
                        'category' => $categoryId);
                $this->dbapi->save($category);
            }
        }
        $news = $this->newsRecord;
        $news['data']['id'] = $details['id'];
        $this->dbapi->refresh($news);
        $news['data'] = array('id' => $details['id'], 'title' =>
                $details['title'], 'lang' => $details['lang'], 'text' =>
                $details['text'], 'source' => $details['newsSource'],
                'author' => $details['author'], 'image' =>
                $news['data']['image'], 'created' => $news['data']['created'],
                'edited' => time());
        if (isset($details['removeImage'])) {
            $news['data']['image'] = null;
        }
        $image = &Modules::getModule('Files')->uploadFiles($_FILES,
                $this->filesRoot);
        if (count($image) > 0) {
            $news['data']['image'] = $image[0]['data']['id'];
        }
        $this->dbapi->update($news);
        return $result;
    }

    public function &onSettings(&$details, $source) {
        $result = null;
        Kernel::setRedirect($this->getLink());
        if (!Modules::getModule('Users')->checkRights('News', 'settings')) {
            Modules::getModule('View')->addMessage($this->lang(
                    'insufficentRights'), View::MSG_ERROR);
            return $result;
        }
        $this->filesRoot = $details['filesRoot'];
        $this->categoryRoot = $details['categoryRoot'];
        $file = @fopen($this->getDir('/settings'), 'w');
        if (!is_resource($file)) {
            return $result;
        }
        @fwrite($file, serialize(array('filesRoot' => $this->filesRoot,
                'categoryRoot' => $this->categoryRoot)));
        @fclose($file);
        return $result;
    }

    /**
     * Displays public interface.
     *
     * @param mixes $param Display parameter. Possible values are:
     *        preview - displays preview of news containing some of the
     *                  newest news.
     */
    private function displayPublic($param) {
        if ($param == 'preview') {
            $this->displayPublicPreview();
        } elseif ($param == 'newest') {
            $this->displayPublicNewest();
        } elseif ($param == '') {
            $this->displayPublicContent();
        } elseif (strpos($param, '-') !== false) {
            list($command, $parameter) = split('-', $param, 2);
            if ($command == 'preview') {
                $this->displayPublicPreview((int) $parameter);
            } elseif ($command == 'list') {
                $this->displayPublicList((int) $parameter);
            }
        }
    }

    /**
     * Displays the newest news added.
     *
     */
    private function displayPublicNewest() {
        $cols = array();
        $orderBy = array('!id');
        $id = &$this->dbdriver->select(self::NEWS_TABLE, $cols, '', $orderBy);
        $this->displayPublicNews((int) $id[0]['id']);
    }

    /**
     * Displays public interface for browsing news.
     *
     */
    private function displayPublicList($category) {
        $cols = array('news');
        $newsIds = &$this->dbdriver->select(self::NEWS_CATEGORIES, $cols,
                '`category` = ' . (int) $category);
        foreach ($newsIds as &$newsId) {
            $newsId = $newsId['news'];
        }
        $params = array('filter' => '`id` IN (' . implode(',', $newsIds) .
                ')', 'order' => array('!id'));
        $news = $this->recordList->getList($this->newsRecord, $params);
        foreach ($news as &$item) {
            $params = array('title' => $item['title'], 'text' => $item['text'],
                    'more' => $this->lang('more'), 'url' => $item['source'],
                    'created' => $item['created']);
            $this->template('articlepreview', $params);
        }
    }

    /**
     * Displays news article.
     *
     * @param int $id ID of the news article to be displayed.
     */
    private function displayPublicNews($id) {
        if (Cache::startOutputCache('News-news', $id)) {
            return;
        }
        $news = $this->newsRecord;
        $news['data']['id'] = $id;
        $this->dbapi->refresh($news);
        $image = &Modules::getModule('Files')->getFile($news['data']['image']);
        $image = '/modules/Files/uploaded/' . $image['data']['localName'];
        if (is_file(Config::ROOT . $image)) {
            $details = getimagesize(Config::ROOT . $image);
            if ($details[0] > 560) {
                $originalImage = Config::WEB_DIR . $image;
                $image = array(array('cmd' => 'import', 0 => Config::ROOT .
                        $image), array('cmd' => 'resize', 0 => 560, 1 =>
                        $details[1], 2 => false, 3 => -1, 4 => true));
                $image = Kernel::getImageLink($image);
            } else {
                $image = Config::WEB_DIR . $image;
                $originalImage = false;
            }
        } else {
            $image = false;
            $originalImage = false;
        }
        $params = array('title' => $news['data']['title'], 'text' =>
                $news['data']['text'], 'image' => $image, 'originalImage' =>
                $originalImage);
        $this->template('article', $params);
        Cache::endOutputCache('News-news');
    }

    /**
     * Displays public interface for showing selected news.
     *
     */
    private function displayPublicContent() {
        $url = Modules::getModule('View')->getUrl();
        if (isset($url[1]) && (strpos($url[1], '-') !== false)) {
            $id = (int) substr($url[1], strrpos($url[1], '-') + 1);
            $this->displayPublicNews($id);
        } else {
            $page = isset($url[1]) ? (int) $url[1] : 1;
            $onPage = 4;
            $pages = array('COUNT(`id`)');
            $pages = &$this->dbdriver->select(self::NEWS_TABLE, $pages);
            $pages = ceil($pages[0]['COUNT(`id`)'] / $onPage);
            $page = max(0, min($page, $pages));
            $order = array('!id');
            $news = &$this->dbapi->load($this->newsRecord, '', $order,
                    ($page - 1) * $onPage, $onPage);
            foreach ($news as &$item) {
                $this->template('articlepreview', $item['data']);
            }
            $params = array('linkPrefix' => $this->getLink(), 'linkSuffix' =>
                    '/', 'current' => $page, 'count' => $pages);
            $this->template('pages', $params);
        }
    }

    /**
     * Displayes preview of newest news available.
     *
     * @param mixed Category ID number to display.
     */
    private function displayPublicPreview($category = null) {
        $params = array('order' => array('!id'), 'count' => 2);
        if (is_numeric($category)) {
            $cols = array('news');
            $newsIDs = &$this->dbdriver->select(self::NEWS_CATEGORIES, $cols,
                    '`category` = ' . (int) $category);
            foreach ($newsIDs as &$newsID) {
                $newsID = $newsID['news'];
            }
            $params['filter'] = '`id` IN (' . implode(',', $newsIDs) . ')';
            $news = &$this->recordList->getList($this->newsRecord, $params);
        } else {
            $news = &$this->recordList->getList($this->newsRecord, $params);
        }
        foreach ($news as &$newsItem) {
            $newsItem['text'] = strip_tags($newsItem['text']);
            $params = array('title' => $newsItem['title'], 'text' =>
                    $this->getStringPreview($newsItem['text'], 60) . '...',
                    'url' => $this->getLink(Kernel::createUrl(
                    $newsItem['title']) . '-' . $newsItem['id'] . '/'),
                    'more' => $this->lang('more'));
            $this->template('articlepreview', $params);
        }
    }

    /**
     * Returns preview of given string string long at least $count bytes long
     * ending with complete word (does not split the last word).
     *
     * @param String $string String of which preview should be created.
     * @param int $count At least how long the preview should be.
     * @return String Preview of the string.
     */
    private function getStringPreview($string, $count) {
        if (strlen($string) <= $count) {
            return $string;
        }
        return substr($string, 0, strpos($string, ' ', $count));
    }

    /**
     * Displays module administration interface.
     *
     * @param mixed $param Display parameter.
     */
    private function displayAdmin($param) {
        if (!Modules::getModule('Users')->checkRights('News', 'view admin')) {
            return;
        }
        if ($param == 'submenu') {
            $this->displayAdminSubmenu();
        } else {
            $details = Modules::getModule('View')->getUrl();
            if (count($details) == 2) {
                if ($details[1] == 'add-news') {
                    $this->displayAdminAddNews();
                } elseif ($details['1'] == 'settings') {
                    $this->displayAdminSettings();
                }
            } elseif (count($details) == 1) {
                $this->displayAdminBrowse();
            } elseif (count($details) == 3) {
                $this->displayAdminEdit($details[2]);
            }
        }
    }

    private function displayAdminSettings() {
        if (!Modules::getModule('Users')->checkRights('News', 'settings')) {
            return;
        }
        $categories = &Modules::getModule('Categories')->getCategoryList();
        $fileCategories = &Modules::getModule('Files')->getCategories();
        $params = array('fileRootLabel' => $this->lang('fileRoot'),
                'categoryRootLabel' => $this->lang('categoryRoot'),
                'fileRoot' => $this->filesRoot, 'categoryRoot' =>
                $this->categoryRoot, 'categories' => $categories, 'files' =>
                $fileCategories, 'save' => $this->lang('save'), 'formStart' =>
                $this->getForm('settings', true, 'News'), 'settings' =>
                $this->lang('settings'));
        $this->template('settings', $params);
    }

    private function displayAdminEdit($id) {
        if (!Modules::getModule('Users')->checkRights('News', 'edit news')) {
            return;
        }
        $news = $this->newsRecord;
        $news['data']['id'] = (int) $id;
        $this->dbapi->refresh($news);
        $categories = &Modules::getModule('Categories')->getCategoryList(
                $this->categoryRoot);
        $params = array('filter' => '`news` = ' . $news['data']['id']);
        $newsCategories = $this->recordList->getList($this->categoryRecord,
                $params);
        foreach ($categories as &$category) {
            $category = $category['data'];
            $selected = false;
            foreach ($newsCategories as $newsCategory) {
                if ($newsCategory['category'] == $category['id']) {
                    $selected = true;
                    break;
                }
            }
            $category['selected'] = $selected;
        }
        if ($news['data']['image'] != null) {
            $news['data']['image'] = Modules::getModule('Files')->getFile(
                    $news['data']['image']);
            $news['data']['image'] = Config::ROOT .
                    '/modules/Files/uploaded/' .
                    $news['data']['image']['data']['localName'];
            $params = array(array('cmd' => 'import', 0 =>
                    $news['data']['image']), array('cmd' => 'resize', 0 => 100,
                    1 => 100, 2 => false, 3 => -1, 4 => true));
            $news['data']['image'] = Kernel::getImageLink($params);
        }
        $params = array('formStart' => $this->getForm('editNews', true,
                'News'), 'editNews' => $this->lang('editNews'), 'title' =>
                $this->lang('title'), 'language' => $this->lang('language'),
                'image' => $this->lang('image'), 'categoriesLabel' =>
                $this->lang('categories'), 'text' => $this->lang('text'),
                'source' => $this->lang('source'), 'author' =>
                $this->lang('author'), 'save' => $this->lang('save'),
                'categories' => $categories, 'news' => $news['data'],
                'savedImage' => $this->lang('savedImage'), 'deleteImage' =>
                $this->lang('deleteImage'), 'fileCategoryId' =>
                $this->filesRoot);
        $this->template('edit', $params);
    }

    private function displayAdminBrowse() {
        $params = array();
        $news = &$this->recordList->getList($this->newsRecord, $params);
        foreach ($news as &$newsItem) {
            $newsItem['edit'] = $this->getLink("edit/{$newsItem['id']}/");
            $newsItem['delete'] = $this->getLink("{$newsItem['id']}/",
                    'delete');
        }
        $params = array('news' => $news, 'list' => $this->lang('news'),
                'edit' => $this->lang('edit'), 'delete' =>
                $this->lang('delete'));
        $this->template('browse', $params);
    }

    private function displayAdminAddNews() {
        if (!Modules::getModule('Users')->checkRights('News', 'create news')) {
            return;
        }
        $categories = Modules::getModule('Categories')->getCategoryList(
                $this->categoryRoot);
        foreach ($categories as &$category) {
            $category = $category['data'];
        }
        $user = &Modules::getModule('Users')->getUser();
        $params = array('formStart' => $this->getForm('createNews', true,
                'News'), 'addNews' => $this->lang('addNewsForm'), 'title' =>
                $this->lang('title'), 'language' => $this->lang('language'),
                'image' => $this->lang('image'), 'categoriesLabel' =>
                $this->lang('categories'), 'addCategory' =>
                $this->lang('addCategory'), 'text' => $this->lang('text'),
                'create' => $this->lang('create'), 'categories' =>
                $categories, 'source' => $this->lang('source'), 'author' =>
                $this->lang('author'), 'login' => $user['data']['login'],
                'fileCategoryId' => $this->filesRoot);
        $this->template('add', $params);
    }

    /**
     * Displays module administration submenu.
     *
     */
    private function displayAdminSubmenu() {
        $params = array('menu' => array(array('href' => $this->getLink(),
                'title' => $this->lang('listTitle'), 'label' =>
                $this->lang('list')), array('href' =>
                $this->getLink('add-news/'), 'title' =>
                $this->lang('addNewsTitle'), 'label' =>
                $this->lang('addNews')), array('href' =>
                $this->getLink('settings/'), 'title' =>
                $this->lang('settingsTitle'), 'label' =>
                $this->lang('settings'))));
        $this->template('submenu', $params);
    }
}
?>
